﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using BreakOutBattles.Sound;
using BreakOutBattles.Game;
#endregion

namespace BreakOutBattles.Menus
{
    /// <summary>
    /// A base class for all menus in the game.
    /// </summary>
    public abstract class Menu : DrawableGameComponent
    {

        #region Menu Item
        /// <summary>
        /// Represents a menu item on a menu. A menu item can be a button or another kind of object
        /// able to interacte with the player.
        /// </summary>
        public class MenuItem
        {
            #region MenuItem Fields
            private string caption = string.Empty;
            private bool selected = false;
            private Vector2 location;
            private Action onEnterAction;
            #endregion

            public MenuItem(string caption, Vector2 location, Action onEnterAction)
            {
                this.caption = caption;
                this.location = location;
                this.onEnterAction = onEnterAction;
            }

            #region Properties
            /// <summary>
            /// Gets and sets the caption of this menu item.
            /// </summary>
            public string Caption
            {
                get
                {
                    return caption;
                }
                set
                {
                    caption = value;
                }
            }

            /// <summary>
            /// Gets and sets the location of this menu item.
            /// </summary>
            public Vector2 Location
            {
                get
                {
                    return location;
                }
                set
                {
                    location = value;
                }
            }

            /// <summary>
            /// Gets and sets the selection status of this menu item.
            /// </summary>
            public bool Selected
            {
                get
                {
                    return selected;
                }
                set
                {
                    selected = value;
                }
            }

            /// <summary>
            /// Gets the action to run when entering this menu option.
            /// </summary>
            public Action OnEnterAction
            {
                get { return onEnterAction; }
            }

            /// <summary>
            /// Invokes the action to run when this menu item has been activated
            /// </summary>
            public void Invoke()
            {
                onEnterAction.Invoke();
            }
            #endregion
        }
        #endregion

        #region Fields
        protected SpriteFont font;
        protected Texture2D background;
        protected List<MenuItem> menuItems;
        protected SpriteBatch spriteBatch;
        protected int currentMenuIndex = 0;

        protected KeyboardState keyboardState;
        protected KeyboardState prevKeyboardState;
        #endregion

        
        public Menu(Microsoft.Xna.Framework.Game game, SpriteBatch spriteBatch)
            : base(game)
        {
            this.spriteBatch = spriteBatch;
            Initialize();
        }

        /// <summary>
        /// Initializes the components of the menu.
        /// </summary>
        public override void  Initialize()
        {
            menuItems = new List<MenuItem>();
            base.Initialize();
            CreateMenu();
        }

        /// <summary>
        /// Loads menu-related content.
        /// </summary>
        public virtual void LoadContent()
        {
            font = Game.Content.Load<SpriteFont>(@"Font\headingFont");
        }

        /// <summary>
        /// A method that allows the menu to create it contents e.g. menu buttons.
        /// </summary>
        public virtual void CreateMenu()
        { }

        /// <summary>
        /// Updates the menu.
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            keyboardState = Keyboard.GetState();
            HandleInput(gameTime);
            prevKeyboardState = Keyboard.GetState();
        }

        /// <summary>
        /// Checks if a specific keyboard key has been pressed.
        /// </summary>
        /// <param name="key">The key to check.</param>
        /// <returns>True if the specified key has been pressed, false otherwise.</returns>
        protected bool KeyPressed(Keys key)
        {
            return keyboardState.IsKeyDown(key) && prevKeyboardState.IsKeyUp(key);
        }

        protected bool KeyReleased(Keys key)
        {
            return keyboardState.IsKeyUp(key) && prevKeyboardState.IsKeyUp(key);
        }

        /// <summary>
        /// Gets the index of the last menu item in the menu.
        /// </summary>
        protected int LastMenuIndex
        {
            get { return menuItems.IndexOf(menuItems.Last()); }
        }

        /// <summary>
        /// Checks if a key is an alpabethical key.
        /// </summary>
        /// <param name="key">The key to check.</param>
        /// <returns>true if the key is alpabethical, false otherwise</returns>
        protected bool IsAlphabeticalKey(Keys key)
        {
            return ((int)key >= 65 && (int)key <= 126)
                    || ((int)key >= 48 && (int)key <= 57)
                    || ((int)key == 32)
                    || (key == Keys.OemPeriod);
        }

        /// <summary>
        /// Handles user interaction with the menu.
        /// </summary>
        protected virtual void HandleInput(GameTime gameTime)
        {
            if (KeyPressed(Keys.Enter))
            {
                Enter();
            }
            else if (KeyPressed(Keys.Up))
            {
                SelectPreviousMenuItem();
                SoundManager.PlayEffect(SoundEffects.MENU_NAVIGATE);
            }
            else if (KeyPressed(Keys.Down))
            {
                SelectNextMenuItem();
                SoundManager.PlayEffect(SoundEffects.MENU_NAVIGATE);
            }
        }

        /// <summary>
        /// Sets the previous menu item as current.
        /// </summary>
        protected virtual void SelectPreviousMenuItem()
        {
            currentMenuIndex = (int)MathHelper.Clamp(currentMenuIndex - 1, 0, menuItems.Count) % menuItems.Count;
        }

        /// <summary>
        /// Sets the next menu item as current.
        /// </summary>
        protected virtual void SelectNextMenuItem()
        {
            currentMenuIndex = (currentMenuIndex + 1) % menuItems.Count;
        }

        /// <summary>
        /// Notifies the current menu item to invoke its assigned action.
        /// </summary>
        protected virtual void Enter()
        {
            menuItems[currentMenuIndex].Invoke();
        }

        /// <summary>
        /// Renders the menu.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            
            foreach (MenuItem menuItem in menuItems)
            {
                if (menuItem == menuItems[currentMenuIndex])
                {
                    spriteBatch.DrawString(font, menuItem.Caption, menuItem.Location, Color.Red);
                }
                else
                {
                    spriteBatch.DrawString(font, menuItem.Caption, menuItem.Location, Color.White);
                }
            }
        }
    }
}
